<html><head><meta charset="utf-8"/><title>函数定义和调用</title></head><body><h1>函数定义和调用</h1><div class="x-wiki-content x-main-content">
 <h3>
  定义函数
 </h3>
 <p>
  在JavaScript中，定义函数的方式如下：
 </p>
 <pre><code>function abs(x) {
    if (x &gt;= 0) {
        return x;
    } else {
        return -x;
    }
}
</code></pre>
 <p>
  上述
  <code>
   abs()
  </code>
  函数的定义如下：
 </p>
 <ul>
  <li>
   <code>
    function
   </code>
   指出这是一个函数定义；
  </li>
  <li>
   <code>
    abs
   </code>
   是函数的名称；
  </li>
  <li>
   <code>
    (x)
   </code>
   括号内列出函数的参数，多个参数以
   <code>
    ,
   </code>
   分隔；
  </li>
  <li>
   <code>
    { ... }
   </code>
   之间的代码是函数体，可以包含若干语句，甚至可以没有任何语句。
  </li>
 </ul>
 <p>
  请注意，函数体内部的语句在执行时，一旦执行到
  <code>
   return
  </code>
  时，函数就执行完毕，并将结果返回。因此，函数内部通过条件判断和循环可以实现非常复杂的逻辑。
 </p>
 <p>
  如果没有
  <code>
   return
  </code>
  语句，函数执行完毕后也会返回结果，只是结果为
  <code>
   undefined
  </code>
  。
 </p>
 <p>
  由于JavaScript的函数也是一个对象，上述定义的
  <code>
   abs()
  </code>
  函数实际上是一个函数对象，而函数名
  <code>
   abs
  </code>
  可以视为指向该函数的变量。
 </p>
 <p>
  因此，第二种定义函数的方式如下：
 </p>
 <pre><code>var abs = function (x) {
    if (x &gt;= 0) {
        return x;
    } else {
        return -x;
    }
};
</code></pre>
 <p>
  在这种方式下，
  <code>
   function (x) { ... }
  </code>
  是一个匿名函数，它没有函数名。但是，这个匿名函数赋值给了变量
  <code>
   abs
  </code>
  ，所以，通过变量
  <code>
   abs
  </code>
  就可以调用该函数。
 </p>
 <p>
  上述两种定义
  <em>
   完全等价
  </em>
  ，注意第二种方式按照完整语法需要在函数体末尾加一个
  <code>
   ;
  </code>
  ，表示赋值语句结束。
 </p>
 <h3>
  调用函数
 </h3>
 <p>
  调用函数时，按顺序传入参数即可：
 </p>
 <pre><code>abs(10); // 返回10
abs(-9); // 返回9
</code></pre>
 <p>
  由于JavaScript允许传入任意个参数而不影响调用，因此传入的参数比定义的参数多也没有问题，虽然函数内部并不需要这些参数：
 </p>
 <pre><code>abs(10, 'blablabla'); // 返回10
abs(-9, 'haha', 'hehe', null); // 返回9
</code></pre>
 <p>
  传入的参数比定义的少也没有问题：
 </p>
 <pre><code>abs(); // 返回NaN
</code></pre>
 <p>
  此时
  <code>
   abs(x)
  </code>
  函数的参数
  <code>
   x
  </code>
  将收到
  <code>
   undefined
  </code>
  ，计算结果为
  <code>
   NaN
  </code>
  。
 </p>
 <p>
  要避免收到
  <code>
   undefined
  </code>
  ，可以对参数进行检查：
 </p>
 <pre><code>function abs(x) {
    if (typeof x !== 'number') {
        throw 'Not a number';
    }
    if (x &gt;= 0) {
        return x;
    } else {
        return -x;
    }
}
</code></pre>
 <h3>
  arguments
 </h3>
 <p>
  JavaScript还有一个免费赠送的关键字
  <code>
   arguments
  </code>
  ，它只在函数内部起作用，并且永远指向当前函数的调用者传入的所有参数。
  <code>
   arguments
  </code>
  类似
  <code>
   Array
  </code>
  但它不是一个
  <code>
   Array
  </code>
  ：
 </p>
 <pre><code class="language-x-javascript">'use strict'
----
function foo(x) {
    console.log('x = ' + x); // 10
    for (var i=0; i&lt;arguments.length; i++) {
        console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
    }
}
foo(10, 20, 30);
</code></pre>
 <p>
  利用
  <code>
   arguments
  </code>
  ，你可以获得调用者传入的所有参数。也就是说，即使函数不定义任何参数，还是可以拿到参数的值：
 </p>
 <pre><code>function abs() {
    if (arguments.length === 0) {
        return 0;
    }
    var x = arguments[0];
    return x &gt;= 0 ? x : -x;
}

abs(); // 0
abs(10); // 10
abs(-9); // 9
</code></pre>
 <p>
  实际上
  <code>
   arguments
  </code>
  最常用于判断传入参数的个数。你可能会看到这样的写法：
 </p>
 <pre><code>// foo(a[, b], c)
// 接收2~3个参数，b是可选参数，如果只传2个参数，b默认为null：
function foo(a, b, c) {
    if (arguments.length === 2) {
        // 实际拿到的参数是a和b，c为undefined
        c = b; // 把b赋给c
        b = null; // b变为默认值
    }
    // ...
}
</code></pre>
 <p>
  要把中间的参数
  <code>
   b
  </code>
  变为“可选”参数，就只能通过
  <code>
   arguments
  </code>
  判断，然后重新调整参数并赋值。
 </p>
 <h3>
  rest参数
 </h3>
 <p>
  由于JavaScript函数允许接收任意个参数，于是我们就不得不用
  <code>
   arguments
  </code>
  来获取所有参数：
 </p>
 <pre><code>function foo(a, b) {
    var i, rest = [];
    if (arguments.length &gt; 2) {
        for (i = 2; i&lt;arguments.length; i++) {
            rest.push(arguments[i]);
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}
</code></pre>
 <p>
  为了获取除了已定义参数
  <code>
   a
  </code>
  、
  <code>
   b
  </code>
  之外的参数，我们不得不用
  <code>
   arguments
  </code>
  ，并且循环要从索引
  <code>
   2
  </code>
  开始以便排除前两个参数，这种写法很别扭，只是为了获得额外的
  <code>
   rest
  </code>
  参数，有没有更好的方法？
 </p>
 <p>
  ES6标准引入了rest参数，上面的函数可以改写为：
 </p>
 <pre><code>function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

foo(1);
// 结果:
// a = 1
// b = undefined
// Array []
</code></pre>
 <p>
  rest参数只能写在最后，前面用
  <code>
   ...
  </code>
  标识，从运行结果可知，传入的参数先绑定
  <code>
   a
  </code>
  、
  <code>
   b
  </code>
  ，多余的参数以数组形式交给变量
  <code>
   rest
  </code>
  ，所以，不再需要
  <code>
   arguments
  </code>
  我们就获取了全部参数。
 </p>
 <p>
  如果传入的参数连正常定义的参数都没填满，也不要紧，rest参数会接收一个空数组（注意不是
  <code>
   undefined
  </code>
  ）。
 </p>
 <p>
  因为rest参数是ES6新标准，所以你需要测试一下浏览器是否支持。请用rest参数编写一个
  <code>
   sum()
  </code>
  函数，接收任意个参数并返回它们的和：
 </p>
 <pre><code class="language-x-javascript">'use strict';
----
function sum(...rest) {
   ???
}
----
// 测试:
var i, args = [];
for (i=1; i&lt;=100; i++) {
    args.push(i);
}
if (sum() !== 0) {
    console.log('测试失败: sum() = ' + sum());
} else if (sum(1) !== 1) {
    console.log('测试失败: sum(1) = ' + sum(1));
} else if (sum(2, 3) !== 5) {
    console.log('测试失败: sum(2, 3) = ' + sum(2, 3));
} else if (sum.apply(null, args) !== 5050) {
    console.log('测试失败: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args));
} else {
    console.log('测试通过!');
}
</code></pre>
 <h3>
  小心你的return语句
 </h3>
 <p>
  前面我们讲到了JavaScript引擎有一个在行末自动添加分号的机制，这可能让你栽到return语句的一个大坑：
 </p>
 <pre><code>function foo() {
    return { name: 'foo' };
}

foo(); // { name: 'foo' }
</code></pre>
 <p>
  如果把return语句拆成两行：
 </p>
 <pre><code>function foo() {
    return
        { name: 'foo' };
}

foo(); // undefined
</code></pre>
 <p>
  <em>
   要小心了
  </em>
  ，由于JavaScript引擎在行末自动添加分号的机制，上面的代码实际上变成了：
 </p>
 <pre><code>function foo() {
    return; // 自动添加了分号，相当于return undefined;
        { name: 'foo' }; // 这行语句已经没法执行到了
}
</code></pre>
 <p>
  所以正确的多行写法是：
 </p>
 <pre><code>function foo() {
    return { // 这里不会自动加分号，因为{表示语句尚未结束
        name: 'foo'
    };
}
</code></pre>
 <h3>
  练习
 </h3>
 <p>
  定义一个计算圆面积的函数
  <code>
   area_of_circle()
  </code>
  ，它有两个参数：
 </p>
 <ul>
  <li>
   r: 表示圆的半径；
  </li>
  <li>
   pi: 表示π的值，如果不传，则默认3.14
  </li>
 </ul>
 <pre><code class="language-x-javascript">'use strict';

function area_of_circle(r, pi) {
----
    return 0;
----
}
// 测试:
if (area_of_circle(2) === 12.56 &amp;&amp; area_of_circle(2, 3.1416) === 12.5664) {
    console.log('测试通过');
} else {
    console.log('测试失败');
}
</code></pre>
 <p>
  小明是一个JavaScript新手，他写了一个
  <code>
   max()
  </code>
  函数，返回两个数中较大的那个：
 </p>
 <pre><code class="language-x-javascript">'use strict';

function max(a, b) {
----
    if (a &gt; b) {
        return
                a;
    } else {
        return
                b;
    }
----
}
console.log(max(15, 20));
</code></pre>
 <p>
  但是小明抱怨他的浏览器出问题了，无论传入什么数，
  <code>
   max()
  </code>
  函数总是返回
  <code>
   undefined
  </code>
  。请帮他指出问题并修复。
 </p>
</div>
</body></html>